/*******************************************************************************
 * Copyright 2011 alipay.com
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/

package com.alipay.platform.data.impl.orm;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Set;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;

import com.alipay.platform.core.AlipayApp;
import com.alipay.platform.data.impl.DataAccessor;

/**
 * 对象关系映射 数据持久化 访问器
 * @author sanping.li@alipay.com
 *
 */
public class OrmDataAccessor implements DataAccessor {
    private DatabaseMgr mDatabaseMgr;
    private SQLiteDatabase mDatabase;
    private OrmHelper mOrmHelper;

    public OrmDataAccessor(Context context) {
        mOrmHelper = new OrmHelper(context);
        AlipayApp alipayApp = (AlipayApp) context.getApplicationContext();
        alipayApp.setOrmHelper(mOrmHelper);
        mDatabaseMgr = new DatabaseMgr(mOrmHelper, context);
        mDatabase = mDatabaseMgr.getWritableDatabase();
    }

    @Override
    public ArrayList<Object> read(Class<?> clazz, String selection, String[] selectionArgs,
                                  String sortOrder) throws Exception {
        ArrayList<Object> result = null;

        String tableName = mOrmHelper.getTableName(clazz.getSimpleName());
        Field[] fields = clazz.getDeclaredFields();
        TableColumn column = null;

        HashMap<String, Field> projections = new HashMap<String, Field>();
        for (Field f : fields) {
            column = f.getAnnotation(TableColumn.class);
            if (column != null) {
                projections.put(column.column(), f);
            }
        }

        if (projections.size() > 0) {
            Set<String> keys = projections.keySet();
            String[] projection = new String[keys.size()];
            keys.toArray(projection);

            Cursor cursor = mDatabase.query(tableName, projection, selection, selectionArgs, null,
                null, sortOrder);
            int num = cursor.getCount();
            Field f = null;
            if (num > 0) {
                cursor.moveToFirst();
                result = new ArrayList<Object>();
                do {
                    Object data = clazz.newInstance();

                    for (String key : keys) {
                        int index = cursor.getColumnIndex(key);
                        f = projections.get(key);
                        f.setAccessible(true);
                        setData(data, f, cursor, index);
                    }
                    result.add(data);
                } while (cursor.moveToNext());
            }
        }
        return result;
    }

    @Override
    public long write(Object object) throws Exception {
        Class<?> clazz = object.getClass();
        Field[] fields = clazz.getDeclaredFields();
        TableColumn column = null;

        String tableName = mOrmHelper.getTableName(clazz.getSimpleName());

        ContentValues values = new ContentValues();

        StringBuffer selection = new StringBuffer();
        ArrayList<String> selectionArgs = new ArrayList<String>();

        String name = null;
        //		String type = null;
        String value = null;
        for (Field f : fields) {
            column = f.getAnnotation(TableColumn.class);
            if (column != null) {
                f.setAccessible(true);
                name = column.column();
                //				type = column.type();

                if ((value = getData(object, f)) != null) {
                    values.put(name, value);
                } else {
                    long id = write(f.get(object));
                    value = id + "";
                    if (id != -1) {
                        values.put(name, value);
                    }
                }

                if (column.essential()) {
                    selection.append(name + " =? AND ");
                    selectionArgs.add(value);
                }
            }
        }

        int size = selectionArgs.size();//判断对象相同的参数个数
        if (size > 0) {
            selection.delete(selection.length() - 4, selection.length());//去掉多余的AND
            String[] strings = new String[size];
            selectionArgs.toArray(strings);

            Cursor cursor = mDatabase.query(tableName, new String[] { "id" }, selection.toString(),
                strings, null, null, null);

            if (cursor.getCount() > 0) {
                cursor.moveToFirst();
                /*int id = */mDatabase.update(tableName, values, selection.toString(), strings);
                return cursor.getLong(0);
            }
        }

        return mDatabase.insert(tableName, null, values);
    }

    @Override
    public int delete(Class<?> clazz, String selection, String[] selectionArgs) {
        String tableName = mOrmHelper.getTableName(clazz.getSimpleName());

        HashMap<String, Field> map = getForeignKey(clazz.getDeclaredFields());
        Set<String> keys = map.keySet();
        String[] projection = new String[keys.size()];
        keys.toArray(projection);

        Cursor cursor = mDatabase.query(tableName, projection, selection, selectionArgs, null,
            null, null);

        if (cursor.getCount() > 0) {//删除关联数据
            cursor.moveToFirst();
            do {
                for (String key : keys) {
                    int index = cursor.getColumnIndex(key);
                    int id = cursor.getInt(index);
                    delete(map.get(key).getType(), "id = ? ", new String[] { "" + id });
                }
            } while (cursor.moveToNext());
        }

        return mDatabase.delete(tableName, selection, selectionArgs);
    }

    /**
     * 获取外键的字段
     * @param fields
     * @return
     */
    private HashMap<String, Field> getForeignKey(Field[] fields) {
        HashMap<String, Field> ret = new HashMap<String, Field>();
        Class<?> clazz = null;
        TableColumn column = null;

        String type = null;
        for (Field f : fields) {
            column = f.getAnnotation(TableColumn.class);
            if (column != null) {
                clazz = f.getType();
                type = clazz.getSimpleName();
                if (!clazz.isPrimitive() && !type.equals("String")) {
                    ret.put(column.column(), f);
                }
            }
        }
        return ret;
    }

    /**
     * @param object
     * @param f
     * @param cursor
     * @param index
     * @throws IllegalArgumentException
     * @throws IllegalAccessException
     * @throws InstantiationException 
     */
    private void setData(Object object, Field f, Cursor cursor, int index) throws Exception {
        String type = f.getType().getSimpleName();
        if (type.equals("int") || type.equals("Interger")) {
            f.set(object, cursor.getInt(index));
        } else if (type.equals("short") || type.equals("Short")) {
            f.set(object, cursor.getShort(index));
        } else if (type.equals("double") || type.equals("Double")) {
            f.set(object, cursor.getDouble(index));
        } else if (type.equals("float") || type.equals("Float")) {
            f.set(object, cursor.getFloat(index));
        } else if (type.equals("long") || type.equals("Long")) {
            f.set(object, cursor.getLong(index));
        } else if (type.equals("boolean") || type.equals("Boolean")) {
            f.set(object, cursor.getInt(index) == 1 ? true : false);
        } else if (type.equals("byte") || type.equals("Byte")) {
            f.set(object, (byte) cursor.getInt(index));
        } else if (type.equals("char") || type.equals("Char")) {
            f.set(object, (char) cursor.getInt(index));
        } else if (type.equals("String")) {
            f.set(object, cursor.getString(index));
        } else {
            f.set(object,
                read(f.getType(), "id = ?", new String[] { cursor.getInt(index) + "" }, null)
                    .get(0));
        }
    }

    /**
     * @param object
     * @param f
     * @return
     * @throws IllegalArgumentException
     * @throws IllegalAccessException
     */
    private String getData(Object object, Field f) throws Exception {
        String type = f.getType().getSimpleName();
        if (type.equals("int") || type.equals("Interger")) {
            return f.getInt(object) + "";
        } else if (type.equals("short") || type.equals("Short")) {
            return f.getShort(object) + "";
        } else if (type.equals("double") || type.equals("Double")) {
            return f.getDouble(object) + "";
        } else if (type.equals("float") || type.equals("Float")) {
            return f.getFloat(object) + "";
        } else if (type.equals("long") || type.equals("Long")) {
            return f.getLong(object) + "";
        } else if (type.equals("boolean") || type.equals("Boolean")) {
            return f.getBoolean(object) ? "1" : "0";
        } else if (type.equals("byte") || type.equals("Byte")) {
            return f.getByte(object) + "";
        } else if (type.equals("char") || type.equals("Char")) {
            return f.getChar(object) + "";
        } else if (type.equals("String")) {
            return f.get(object).toString();
        }
        return null;
    }

    @Override
    public String getPath() {
        return mOrmHelper.getDataBase();
    }

    @Override
    public int getVersion() {
        return mOrmHelper.getVersion();
    }

}
